//
//  CGX_PlayTool.m
//  AVPlayer_Audio
//
//  Created by 曹曹 on 16/9/23.
//  Copyright © 2016年 曹曹. All rights reserved.
//

#import "CGX_PlayTool.h"

@interface CGX_PlayTool ()

// 计时器
@property (nonatomic, strong) NSTimer *timer;
// 当前时长
@property (nonatomic, assign) NSInteger currentSecond;
// 总时长
@property (nonatomic, assign) NSInteger itemDuration;
// 播放进度
@property (nonatomic, assign) CGFloat progressValue;
// 缓冲进度
@property (nonatomic, assign) CGFloat loadedDuration;

@end

@implementation CGX_PlayTool

#pragma mark - 单例
+ (instancetype)sharedPlayTool {
    static CGX_PlayTool *tool = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        tool = [[CGX_PlayTool alloc] init];
    });
    return tool;
}
// AVPlayer并没有通过block或者代理向我们返回这一状态(事件)
// 而是向通知中心注册了一条通知(AVPlayerItemDidPlayToEndTimeNotification)
// 我们也只有这一条途径获取播放结束这一事件.
// 所以,在我们创建好一个播放器时([[AVPlayer alloc] init])
// 应该立刻为通知中心添加观察者,来观察这一事件的发生.这个动作放到init里,最及时也最合理.
- (instancetype)init {
    self = [super init];
    if (self) {
        _cgx_player = [[AVPlayer alloc] init];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    }
    return self;
}
#pragma mark - 播放完成后执行的方法
- (void)playerItemDidEnd:(NSNotification *)noti {
    [self pauseAudio];
}
#pragma mark - 更新当前将要播放的音频
- (void)prepareToPlayAudio {
    if (self.cgx_player.currentItem) {
        [self.cgx_player.currentItem removeObserver:self forKeyPath:@"status"];
        [self.cgx_player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
        [self.cgx_player.currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
        [self.cgx_player.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
    }
    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:_audioURL]];
    [item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    [item addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
    [item addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
    [self.cgx_player replaceCurrentItemWithPlayerItem:item];
}
#pragma mark - 观察者的处理方法, 观察的是Item的Status状态
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    AVPlayerItem *playerItem = (AVPlayerItem *)object;
    /* 监听音频文件的状态 */
    if ([keyPath isEqualToString:@"status"]) {
        switch (playerItem.status) {
            case AVPlayerItemStatusUnknown | AVPlayerItemStatusFailed:
                NSLog(@"没有准备好, 无法播放");
                break;
            case AVPlayerItemStatusReadyToPlay: {
//                NSInteger duration = playerItem.duration.value / playerItem.duration.timescale; //视频总时间
//                NSLog(@"准备好播放了，总时间：%.2ld分:%.2ld秒", duration / 60, duration % 60);//还可以获得播放的进度，这里可以给播放进度条赋值了
            }
                break;
            default:
                break;
        }
        /* 监听播放器的缓冲进度 */
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        /* 在监听方法中将缓冲进度通过代理方法提供给外部使用 */
        [self.delegate getItemLoadedDuration:self.loadedDuration];
        /* 监听播放器在缓冲数据的状态 */
    } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {
        NSLog(@"缓冲不足,无法开始准备");
    } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
        NSLog(@"缓冲足够, 可以开始准备");
        /* 由于 AVPlayer 缓存不足就会自动暂停，所以缓存充足了需要手动播放，才能继续播放 */
        [self playAudio];
    }
}
#pragma mark - 播放
- (void)playAudio {
    //如果计时器已经存在, 说明已经在播放中, 直接返回.
    //对于已经存在的计时器, 只有musicpause方法才会停止和注销
    if (self.timer != nil) {
        return;
    }
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
    [self.cgx_player play];
}
#pragma mark - 通过代理实时传递数据给外部使用
- (void)timerAction:(NSTimer *)timer {
    if (self.delegate) {
        [self.delegate getItemDuration:[self getStringValue:self.itemDuration] currentSecond:[self getStringValue:self.currentSecond] currentProgress:self.progressValue];
    }
}
#pragma mark - 暂停
- (void)pauseAudio {
    [self.timer invalidate];
    self.timer = nil;
    [self.cgx_player pause];
}
#pragma mark - 调整进度
- (void)seekToDragedValue:(CGFloat)value {
    [self.cgx_player pause];
    double time = value * self.itemDuration;
    [self.cgx_player seekToTime:CMTimeMake(time, 1) completionHandler:^(BOOL finished) {
        if (finished) {
            [self.cgx_player play];
        }
    }];
}
#pragma mark - getter 当前时长
- (NSInteger)currentSecond {
    if (self.cgx_player.currentItem) {
        return self.cgx_player.currentTime.value / self.cgx_player.currentTime.timescale;
    } else {
        return 0;
    }
}
#pragma mark - getter 总时长
- (NSInteger)itemDuration {
    CMTime totalTime = [self.cgx_player.currentItem duration];
    if (totalTime.timescale == 0) {
        return 1;
    } else {
        return totalTime.value / totalTime.timescale;
    }
}
#pragma mark - getter 进度
- (CGFloat)progressValue {
    return (CGFloat)self.currentSecond / self.itemDuration;
}
#pragma mark - 缓冲进度
- (CGFloat)loadedDuration {
    AVPlayerItem *playerItem = self.cgx_player.currentItem;
    NSArray *loadedTimeRanges = [playerItem loadedTimeRanges];
    /** 获取缓冲区域 */
    CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];
    float startSeconds = CMTimeGetSeconds(timeRange.start);
    float durationSeconds = CMTimeGetSeconds(timeRange.duration);
    /** 计算已经缓冲的时间 */
    NSTimeInterval timeInterval = startSeconds + durationSeconds;
    /** 总时间结构体 */
    CMTime duration = playerItem.duration;
    /** 总时间 */
    CGFloat totalDuration = CMTimeGetSeconds(duration);
    return timeInterval / totalDuration;
}
#pragma mark - 将小数改成字符串
- (NSString *)getStringValue:(NSInteger)value {
    return [NSString stringWithFormat:@"%.2ld:%.2ld", value / 60, value % 60];
}

@end
